﻿using System;   
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;

namespace Mimoza.AgentLayer
{
    /// <summary>
    /// Class deals with database of agent.
    /// </summary>
    public class AgentDatabase : BaseObject
    {
        const string c_SyncedPolicyId = "731088DF-8D59-4907-A5D6-498B336FEB24";

        public AgentDatabase(Configuration config)
            : base(config)
        {
        }

        override public void Init()
        {
            LoadHostInfo();
        }

        override public void Shutdown()
        {
        }

        public void SaveHostSettings()
        {
            Common.Logger.Log.Info("AgentDatabase.SaveHostSettings...");

            Settings.Save(Configuration.Settings);
            
            ObjectLayer.Host host = GetLocalHost();
        
            PutHostSettings(host);

            ObjectLayer.HostRepository.Update(host);

            Common.Logger.Log.Info("AgentDatabase.SaveHostSettings...OK");
        }

        public void LoadHostInfo()
        {
            Settings.Load(Configuration.Settings);
        }

        public void CreatLocalHost(string newHostId)
        {
            ObjectLayer.Host host = GetLocalHost();
            if (host != null) return;

            Common.Logger.Log.Info("AgentDatabase.CreatLocalHost newHostId - '" + newHostId.ToString() + "'...");

            ObjectLayer.Container localHostContainer =
                GetLocalHostContainer();
            if (localHostContainer == null)
            {
                return;
            }

            host = ObjectLayer.HostRepository.CreateHost(new Guid(newHostId));
            host.Parent = localHostContainer;

            ObjectLayer.HostRepository.Add(host);
        }

        public ObjectLayer.Host GetLocalHost()
        {
            ObjectLayer.Container localHostContainer = 
                GetLocalHostContainer();
            if (localHostContainer == null)
            {
                MakeDatabaseStructure();
                localHostContainer = GetLocalHostContainer();
            }

            IEnumerable<ObjectLayer.Host> hosts = localHostContainer.GetHosts();

            foreach (ObjectLayer.Host host in hosts)
            {
                return host; // local host has inly one host record in container
            }

            return null;
        }

        public void SaveLocalHostModificationId(Guid modificationId)
        {
            ObjectLayer.Host localHost = GetLocalHost();
            localHost.ModificationID = modificationId;

            ObjectLayer.HostRepository.Update(localHost);
        }

        public Guid GetLocalHostModificationId()
        {
            ObjectLayer.Host localHost = GetLocalHost();
            return localHost.ModificationID;
        }

        public Guid GetHostEffectivePolicyId()
        {
            ObjectLayer.Container rootContainer =
                ObjectLayer.ContainerRepository.RootContainer;

            return rootContainer.EffectivePolicyID;
        }

        public void SaveHostEffectivePolicyId(Guid newEffectivePolicyId)
        {
            ObjectLayer.Container rootContainer =
                ObjectLayer.ContainerRepository.RootContainer;

            rootContainer.EffectivePolicyID = newEffectivePolicyId;

            ObjectLayer.ContainerRepository.Update(rootContainer);
        }

        public Guid GetSyncEffectivePolicyId()
        {
            ObjectLayer.Container syncContainer = GetSyncHostContainer();

            return syncContainer.EffectivePolicyID;
        }

        public void SaveSyncEffectivePolicyId(Guid newEffectivePolicyId)
        {
            ObjectLayer.Container syncContainer = GetSyncHostContainer();

            syncContainer.EffectivePolicyID = newEffectivePolicyId;

            ObjectLayer.ContainerRepository.Update(syncContainer);

            Common.Logger.Log.Info("AgentDatabase.SaveSyncEffectivePolicyId. syncId - '" + newEffectivePolicyId.ToString() + "'.");
        }

        public List<ObjectLayer.Policy> GetRootPolicies()
        {
            ObjectLayer.Container rootContainer =
                ObjectLayer.ContainerRepository.RootContainer;

            return new List<Mimoza.ObjectLayer.Policy>(rootContainer.GetDirectPolicy());
        }

        public List<ObjectLayer.Policy> GetSyncPolicies()
        {
            ObjectLayer.Policy syndDataPolicy = GetSyncDataPolicy();
            List<Common.PolicyRecord> vecPoliciesRes = SerializeSyncData(syndDataPolicy.Data);

            List<ObjectLayer.Policy> policies = new List<Mimoza.ObjectLayer.Policy>();
            foreach (Common.PolicyRecord polRec in vecPoliciesRes)
                policies.Add( new ObjectLayer.Policy(null, polRec) );

            return policies;
        }

        public void SetSyncPolicies(List<ObjectLayer.Policy> policies)
        {
            List<Common.PolicyRecord> vecPoliciesRes = new List<Mimoza.Common.PolicyRecord>();
            foreach (ObjectLayer.Policy pol in policies) vecPoliciesRes.Add(pol.Record);

            ObjectLayer.Policy syndDataPolicy = GetSyncDataPolicy();
            syndDataPolicy.Data = SerializeSyncData(vecPoliciesRes);
            ObjectLayer.PolicyRepository.Update(syndDataPolicy);
        }

        public ObjectLayer.Policy GetLocalHostPluginPolicy()
        {
            foreach (ObjectLayer.Policy policy in GetRootPolicies())
            {
                if (policy.PluginID == Mimoza.PluginLayer.ExecutePluginManager.LocalHostPluginId)
                {
                    Common.Logger.Log.Info("AgentDatabase.GetLocalHostPolicy. Policy found id - '" + policy.ID.ToString() + "'.");

                    return policy;
                }
            }

            Common.Logger.Log.Info("AgentDatabase.GetLocalHostPolicy. Create new host Policy.");
            
            ObjectLayer.Policy hostPolicy = ObjectLayer.PolicyRepository.CreatePolicy(Guid.NewGuid());
            hostPolicy.PluginID = Mimoza.PluginLayer.ExecutePluginManager.LocalHostPluginId;
            hostPolicy.Parent = ObjectLayer.ContainerRepository.RootContainer;
            hostPolicy.IsEnabled = true;
            //hostPolicy.IsSystem = true;
            //hostPolicy.IsVisible = false;
            return hostPolicy;
        }

        public void ReplaceServerPolicies(List<ObjectLayer.Policy> policies, Guid syncId)
        {
            List<ObjectLayer.Policy> existsPolicies = GetRootPolicies();

            ObjectLayer.Container rootContainer = ObjectLayer.ContainerRepository.RootContainer;

            foreach (ObjectLayer.Policy policy in policies)
            {
                policy.Parent = rootContainer;
                policy.UpdatesID = Guid.Empty; // clear ref agent doesnt save updates in db

                bool bExist = (existsPolicies.FindIndex(p => p.ID == policy.ID) >= 0);
                if (bExist)
                {
                    ObjectLayer.PolicyRepository.Update(policy);
                }
                else
                {
                    ObjectLayer.PolicyRepository.Add(policy);
                }
            }

            foreach (ObjectLayer.Policy existPolicy in existsPolicies)
            {
                bool bExistInNew = (policies.FindIndex(p => p.ID == existPolicy.ID) >= 0);

                if (!bExistInNew)
                {
                    ObjectLayer.PolicyRepository.Delete(existPolicy);
                }
            }

            SaveHostEffectivePolicyId(syncId);
        }

        public void ReplaceSyncPolicies(List<ObjectLayer.Policy> policies, Guid syncId)
        {
            List<ObjectLayer.Policy> syncPolicies = GetSyncPolicies();

            ObjectLayer.Container syncContainer = GetSyncHostContainer();

            foreach (ObjectLayer.Policy policy in policies)
            {
                int index = syncPolicies.FindIndex(p => p.ID == policy.ID);
                if (index>=0)
                {
                    syncPolicies[index] = policy;
                }
                else
                {
                    syncPolicies.Add(policy);
                }
            }

            for (int i = 0; i < syncPolicies.Count; )
            {
                bool bExistInNew = (policies.FindIndex(p => p.ID == syncPolicies[i].ID) >= 0);

                if (!bExistInNew)
                {
                    syncPolicies.RemoveAt(i);
                }
                else i++;
            }

            SetSyncPolicies(syncPolicies);
            SaveSyncEffectivePolicyId(syncId);
        }

        public ObjectLayer.Container GetLocalHostContainer()
        {
            ObjectLayer.Container rootContainer =
                ObjectLayer.ContainerRepository.RootContainer;

            foreach (ObjectLayer.Container container in rootContainer.GetChilds())
            {
                if (container.IsVisible && !container.IsSystem)
                {
                    return container;
                }
            }
            return null;
        }

        public ObjectLayer.Container GetSyncHostContainer()
        {
            ObjectLayer.Container rootContainer =
                ObjectLayer.ContainerRepository.RootContainer;

            foreach (ObjectLayer.Container container in rootContainer.GetChilds())
            {
                if (!container.IsVisible)
                {
                    return container;
                }
            }
            return null;
        }

        ObjectLayer.Policy GetSyncDataPolicy()
        {   
            ObjectLayer.Policy syncDataPolicy = ObjectLayer.PolicyRepository.GetPolicyByID(new Guid(c_SyncedPolicyId));
            return syncDataPolicy;
        }

        string SerializeSyncData(List<Common.PolicyRecord> vecPoliciesRes)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(List<Common.PolicyRecord>));
            using (TextWriter text = new StringWriter())
            {
                serializer.Serialize(text, vecPoliciesRes);
                return text.ToString();
            }
        }

        List<Common.PolicyRecord> SerializeSyncData(string vecData)
        {
            if (string.IsNullOrEmpty(vecData)) return new List<Mimoza.Common.PolicyRecord>();

            XmlSerializer deserializer = new XmlSerializer(typeof(List<Common.PolicyRecord>));
            using (TextReader text = new StringReader(vecData))
            {
                return deserializer.Deserialize(text) as List<Common.PolicyRecord>;
            }
        }

        void GetHostSettings(ObjectLayer.Host localHost)
        {
        }

        void PutHostSettings(ObjectLayer.Host localHost)
        {
        }

        void MakeDatabaseStructure()
        {
            ObjectLayer.Container rootContainer = 
                ObjectLayer.ContainerRepository.CreateRootContainer();

            ObjectLayer.Container localHostContainer =
                ObjectLayer.ContainerRepository.CreateContainerByID(
                new Guid());

            localHostContainer.Parent = rootContainer;
            FillLocalHostContainer(localHostContainer);
            ObjectLayer.ContainerRepository.Add(localHostContainer);

            ObjectLayer.Container syncContainer =
                ObjectLayer.ContainerRepository.CreateContainerByID(
                new Guid());

            syncContainer.Parent = rootContainer;
            FillSyncContainer(syncContainer);
            ObjectLayer.ContainerRepository.Add(syncContainer);

            AddSyncedPolicy(syncContainer);
        }

        void FillLocalHostContainer(ObjectLayer.Container localHostContainer)
        {
            localHostContainer.Name = "Local : need to fix";
            localHostContainer.IsVisible = true;
        }

        void FillSyncContainer(ObjectLayer.Container syncContainer)
        {
            syncContainer.Name = "Sync container : system and should be hidden";
            syncContainer.IsSystem = true;
            syncContainer.IsVisible = false;
        }

        void AddSyncedPolicy(ObjectLayer.Container syncContainer)
        {
            ObjectLayer.Policy syncDataPolicy = ObjectLayer.PolicyRepository.CreatePolicy(new Guid(c_SyncedPolicyId));
            syncDataPolicy.Name = "SyncDataPolicy";
            syncDataPolicy.Parent = syncContainer;
            syncDataPolicy.IsEnabled = true;
            //syncDataPolicy.IsSystem = true;
            //syncDataPolicy.IsVisible = false;

            ObjectLayer.PolicyRepository.Add(syncDataPolicy);
        }
    }
}
